//#include <svdpi.h>
#include "ram.h"
#include "vcstop.h"
#include "axi.h"
#include "sim_config.h"

#define nullptr 0

/// global data
RAM * sysram = nullptr;
AXISim::AXI_wrapper * axi_bus = nullptr;

VCSTop * master = nullptr;

bool hasInitialize = false;

extern "C" void generate_memory_region() {
    if (!sysram) {
        sysram = new RAM(SYS_RAM_SIZE);
    } else {
        printf("Memory Region has generated!\n");
        exit(1);
    }

    if (!master)
        master = new VCSTop();

    if (!master)
        printf("Failed VCSTop instance!\n");

    if (!axi_bus) {
        axi_bus = new AXISim::AXI_wrapper(master, sysram);
    } else {
        printf("AXI Bus has generated!\n");
        exit(1);
    }

    /// initialize ram
    uint64_t entry_addr = 0;
    sysram->ram_load_elf(kernel_elf_bios, entry_addr);
    if (!entry_addr){
        printf("Load ELF failed!\n");
        exit(1);
    } else {
        INFO(PRINT_BOLDYELLOW, "Load BIOS ELF!\n");
    }

    /// initialize ram
    entry_addr = 0;
    sysram->ram_load_elf(kernel_elf, entry_addr);
    if (!entry_addr){
        printf("Load ELF failed!\n");
        exit(1);
    } else {
        INFO(PRINT_BOLDYELLOW, "Load kernel ELF!\n");
    }

    if (load_bin) {
        sysram->ram_load_binary(bin_addr, bin_name);
    }

}


extern "C" void access_memory_subsystem(
///* INPUT */         unsigned char reset,
///* INPUT */         svBitVecVal *sim_cycles,
/* INPUT */         unsigned char axi_aw_valid,
/* OUTPUT */        unsigned char *axi_aw_ready,
/* INPUT */ const   svBitVecVal *axi_aw_addr,
/* INPUT */ const   svBitVecVal *axi_aw_id,
/* INPUT */ const   svBitVecVal *axi_aw_len,
/* INPUT */ const   svBitVecVal *axi_aw_size,
/* INPUT */ const   svBitVecVal *axi_aw_burst,
/* INPUT */ const   svBitVecVal *axi_aw_cache,
/* INPUT */ const   svBitVecVal *axi_aw_prot,
/* INPUT */         unsigned char axi_w_valid,
/* OUTPUT */        unsigned char *axi_w_ready,
/* INPUT */ const   svBitVecVal *axi_w_data,
/* INPUT */ const   svBitVecVal *axi_w_strb,
/* INPUT */         unsigned char axi_w_last,
/* OUTPUT */        unsigned char *axi_b_valid,
/* INPUT */         unsigned char axi_b_ready,
/* OUTPUT */        svBitVecVal *axi_b_id,
/* OUTPUT */        svBitVecVal *axi_b_resp,
/* INPUT */         unsigned char axi_ar_valid,
/* OUTPUT */        unsigned char *axi_ar_ready,
/* INPUT */ const   svBitVecVal *axi_ar_addr,
/* INPUT */ const   svBitVecVal *axi_ar_id,
/* INPUT */ const   svBitVecVal *axi_ar_len,
/* INPUT */ const   svBitVecVal *axi_ar_size,
/* INPUT */ const   svBitVecVal *axi_ar_burst,
/* INPUT */ const   svBitVecVal *axi_ar_cache,
/* INPUT */ const   svBitVecVal *axi_ar_prot,
/* OUTPUT */        unsigned char *axi_r_valid,
/* INPUT */         unsigned char axi_r_ready,
/* OUTPUT */        svBitVecVal *axi_r_data,
/* OUTPUT */        svBitVecVal *axi_r_id,
/* OUTPUT */        svBitVecVal *axi_r_resp,
/* OUTPUT */        unsigned char *axi_r_last)
{
    uint64_t cycles = 0;//*(uint64_t*)sim_cycles;

//    if (reset) {
//        if (!hasInitialize)
//            INFO(PRINT_BOLDYELLOW,"[MainMemory] Initialized.\n");
//        hasInitialize = true;
//        master->axi_aw_ready = true;
//        master->axi_ar_ready = true;
//        return;
//    }
//
//    if (!hasInitialize)
//        return;

    /// get data from axi bus
    master->axi_aw_addr = *(uint64_t*)axi_aw_addr;
    master->axi_aw_valid = axi_aw_valid;
    master->axi_aw_id = *axi_aw_id;
    master->axi_aw_len = *axi_aw_len;
    master->axi_aw_size = *axi_aw_size;
    master->axi_aw_burst = *axi_aw_burst;
    master->axi_aw_cache = *axi_aw_cache;
    master->axi_aw_prot = *axi_aw_prot;

    master->axi_w_valid = axi_w_valid;
    master->axi_w_data = axi_w_data;
    master->axi_w_strb = *axi_w_strb;
    master->axi_w_last = axi_w_last;

    master->axi_b_ready = axi_b_ready;

    master->axi_ar_valid = axi_ar_valid;
    master->axi_ar_addr = *(uint64_t*)axi_ar_addr;
    master->axi_ar_id = *axi_ar_id;
    master->axi_ar_len = *axi_ar_len;
    master->axi_ar_size = *axi_ar_size;
    master->axi_ar_burst = *axi_ar_burst;
    master->axi_ar_cache = *axi_ar_cache;
    master->axi_ar_prot = *axi_ar_prot;
    master->axi_r_ready = axi_r_ready;


    /// NOTE: firstly get C environment address of
    /// axi_r_data, so subsequently ram can write data
    /// into VCS by hook pointer
    master->axi_r_data = axi_r_data;


    bool debug_point = false;

    if (cycles == 0xb12) {
        debug_point = true;
//        if (!master->skip)
//            master->skip = 1;
//        else
//            return;
    }

    if (axi_ar_valid) {
        if (master->axi_ar_id==0x3f)
            debug_point = true;
    }

    if (master->axi_ar_addr >= 0x102000000) {
        debug_point = true;
    }

    if (axi_aw_valid) {
        debug_point = true;
    }

    if (axi_w_valid) {
        debug_point = true;
        master->axi_w_data = axi_w_data;
    }

    axi_bus->handle_ar(cycles);
    axi_bus->handle_r(cycles);
    axi_bus->handle_aw(cycles);
    axi_bus->handle_w(cycles);
    axi_bus->handle_b(cycles);

    /// we first write back previous cycle data into VCS
    *axi_aw_ready = master->axi_aw_ready;

    *axi_w_ready = master->axi_w_ready;
    
    *axi_b_valid = master->axi_b_valid;
    *axi_b_id = master->axi_b_id;
    *axi_b_resp = master->axi_b_resp;
    
    *axi_ar_ready = master->axi_ar_ready;
    
    *axi_r_valid = master->axi_r_valid;
    // axi_r_data = master->axi_r_data;
    *axi_r_id = master->axi_r_id;
    *axi_r_resp = master->axi_r_resp;
    *axi_r_last = master->axi_r_last;
}